package com.zhanghu.backendZray.controller.user;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zhanghu.backendZray.annotation.LoginNoNeed;
import com.zhanghu.backendZray.annotation.MenuNeed;
import com.zhanghu.backendZray.cache.ContextCache;
import com.zhanghu.backendZray.cache.WxLoginStateCache;
import com.zhanghu.backendZray.constants.CityConstant;
import com.zhanghu.backendZray.constants.CommonConstant;
import com.zhanghu.backendZray.job.MinIOFileExpireHandleJob;
import com.zhanghu.backendZray.manager.property.PropertyManager;
import com.zhanghu.backendZray.manager.role.UserRoleMenuManager;
import com.zhanghu.backendZray.pojo.dto.ApiResponse;
import com.zhanghu.backendZray.pojo.dto.LoginResDTO;
import com.zhanghu.backendZray.pojo.dto.UserTokenInfo;
import com.zhanghu.backendZray.pojo.dto.weather.WeatherAPIDetailDTO;
import com.zhanghu.backendZray.pojo.request.LoginRequest;
import com.zhanghu.backendZray.pojo.request.OnlyIdRequest;
import com.zhanghu.backendZray.pojo.request.city.CitySettingRequest;
import com.zhanghu.backendZray.pojo.request.user.ChangePasswordRequest;
import com.zhanghu.backendZray.pojo.request.user.UserBaseInfoRequest;
import com.zhanghu.backendZray.pojo.request.user.UserPageRequest;
import com.zhanghu.backendZray.pojo.request.user.UserRequest;
import com.zhanghu.backendZray.pojo.vo.UserVo;
import com.zhanghu.backendZray.pojo.vo.WxLoginVo;
import com.zhanghu.backendZray.service.UserService;
import com.zhanghu.backendZray.utils.weather.WeatherUtil;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.request.AuthWeChatOpenRequest;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Optional;

import static com.zhanghu.backendZray.constants.CommonConstant.SUPER_ADMIN_ROLE;

@Data
@RestController
@RequestMapping("user")
@RequiredArgsConstructor
@Validated
@Slf4j
public class UserController {
    private final UserService userService;
    private final UserRoleMenuManager userRoleMenuManager;
    private final PropertyManager propertyManager;
    private final MinIOFileExpireHandleJob minIOFileExpireHandleJob;

    @MenuNeed(menuName = "user_manage")
    @PostMapping("add")
    public ApiResponse add(@Validated(UserRequest.Create.class) @RequestBody UserRequest userRequest) {
        userService.add(userRequest);
        return ApiResponse.ok();
    }

    @MenuNeed(menuName = "user_manage")
    @PostMapping("update")
    public ApiResponse update(@Validated(UserRequest.Update.class) @RequestBody UserRequest userRequest) {
        userService.update(userRequest);
        return ApiResponse.ok();
    }

    /**
     * 修改密码
     */
    @PostMapping("changePassword")
    public ApiResponse changePassword(@RequestBody ChangePasswordRequest changePasswordRequest) {
        userService.changePassword(changePasswordRequest);
        return ApiResponse.ok();
    }

    /**
     * 用户基础信息设置：用户名、手机号 (第一次设置需要设置密码)
     */
    @PostMapping("namePhoneSetting")
    public ApiResponse namePhoneSetting(@RequestBody UserBaseInfoRequest userBaseInfoRequest) {
        userService.namePhoneSetting(userBaseInfoRequest);
        return ApiResponse.ok();
    }

    /**
     * 超管删除用户
     *
     * @param request
     * @return
     */
    @MenuNeed(menuName = "user_manage")
    @PostMapping("delete")
    public ApiResponse delete(@RequestBody @NotNull OnlyIdRequest request) {
        Assert.notNull(request, "id不能为空");
        userService.del(request.getId());
        return ApiResponse.ok();
    }

    /**
     * 分页查询
     *
     * @param
     * @return
     */
    @MenuNeed(menuName = "user_manage")
    @PostMapping("page")
    public ApiResponse<Page<UserVo>> page(@RequestBody UserPageRequest userPageRequest) {
        Page<UserVo> pageInfo = userRoleMenuManager.page(userPageRequest);
        return ApiResponse.ok(pageInfo);
    }

    /**
     * 普通用户名密码登录，返回token
     *
     * @param loginRequest
     * @return
     */
    @PostMapping("normalLogin")
    @LoginNoNeed
    public ApiResponse<LoginResDTO> doLogin(@RequestBody LoginRequest loginRequest) {
        return ApiResponse.ok(userRoleMenuManager.doNormalLogin(loginRequest));
    }

    /**
     * 用户退出登录
     */
    @PostMapping("logout")
    public ApiResponse logout() {
        userRoleMenuManager.logout();
        return ApiResponse.ok();
    }

    /**
     * 超管强制用户下线
     */
    @MenuNeed(menuName = "user_manage")
    @PostMapping("offline")
    public ApiResponse offline(@RequestBody @NotNull OnlyIdRequest request) {
        userRoleMenuManager.offline(request.getId().intValue());
        return ApiResponse.ok();
    }

    /**
     * 微信web扫码登录相关
     * 这种是跳转到一个新的页面（微信授权页的方式）
     */
    /*@RequestMapping("wxRender")
    @LoginNoNeed
    public ApiResponse<String> renderAuth() {
        log.info("enter weixin render...");
        AuthRequest authRequest = getAuthRequest();
        String redirectUrl = authRequest.authorize(AuthStateUtils.createState());
        log.info("redirectUrl:{}", redirectUrl);
        return ApiResponse.ok(redirectUrl);
//        response.sendRedirect(redirectUrl);
    }*/

    /**
     * 微信扫码登录，回调
     * 这种是跳转到一个新的页面（微信授权页的方式）
     */
    /*@RequestMapping("callback")
    @LoginNoNeed
    public ModelAndView login(AuthCallback callback) {
        log.info("enter weixin callback..., callback:{}", JSONObject.toJSONString(callback));
        AuthRequest authRequest = getAuthRequest();
        AuthResponse<AuthUser> response = authRequest.login(callback);
        log.info(JSONObject.toJSONString(response));
        AuthUser user = response.getData();
        LoginResDTO loginResDTO = userRoleMenuManager.doWxLogin(user, callback.getState());
        if (loginResDTO == null) {
            return new ModelAndView("redirect:" + propertyManager.getCallBackRedirectIndexUrl() + "/login");
//            return new ModelAndView("redirect:/login.html");
        }
        // 只写/， 跳转后的页面是类似这样的：http://localhost:20104/weixin/callback
//        ModelAndView modelAndView = new ModelAndView("index");
        // 这样跳，可以实现，但是url里会带有index.html， 直接写redirect:/ 就不会带，也能实现
//        ModelAndView modelAndView = new ModelAndView("redirect:/index.html");
//        ModelAndView modelAndView = new ModelAndView("redirect:/");
        ModelAndView modelAndView = new ModelAndView("redirect:" + propertyManager.getCallBackRedirectIndexUrl());
        String token = loginResDTO.getToken();
        modelAndView.addObject(CommonConstant.TOKEN, token);
        modelAndView.addObject(CommonConstant.MENU_LIST, loginResDTO.getMenuList());
        return modelAndView;
    }*/

    /**
     * 微信web扫码登录相关
     * 这种是内嵌的方式
     */
    @RequestMapping("wxRender")
    @LoginNoNeed
    public ApiResponse<WxLoginVo> renderAuth() {
        String wxClientId = propertyManager.getWxClientId();
        String redirectUrl = propertyManager.getWxNotifyUrl() + "/user/callback";
        String uuid = IdUtil.fastSimpleUUID();
        WxLoginStateCache.putWxLoginState(uuid, "exist");
        return ApiResponse.ok(new WxLoginVo(wxClientId, redirectUrl, uuid));
    }

    /**
     * 微信扫码登录回调
     * 这种是内嵌的方式
     */
    @RequestMapping("callback")
    @LoginNoNeed
    public ModelAndView login(String code, String state) {
        log.info("enter weixin callback..., code:{}, state:{}", code, state);
        if (StrUtil.isEmpty(code) || StrUtil.isEmpty(state)) {
            log.info("enter weixin callback..., invalid callback");
            ModelAndView modelAndView = new ModelAndView("redirect:" + propertyManager.getCallBackRedirectIndexUrl() + "/login");
            modelAndView.addObject(CommonConstant.WX_LOGIN_INVALID_CALLBACK, "invalid callback");
            return modelAndView;
        }
        // 扫码超时
        if (!WxLoginStateCache.isExistState(state)) {
            ModelAndView modelAndView = new ModelAndView("redirect:" + propertyManager.getCallBackRedirectIndexUrl() + "/login");
            modelAndView.addObject(CommonConstant.WX_LOGIN_EXPIRE, 5);
            return modelAndView;
        }
        // code -> accessToken
        String requestUrl = String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
                propertyManager.getWxClientId(), propertyManager.getWxClientSecret(), code);
        String responseBody = HttpRequest.get(requestUrl).execute().body();
        LoginResDTO loginResDTO = userRoleMenuManager.doWxLoginInner(responseBody);
        if (loginResDTO == null) {
            return new ModelAndView("redirect:" + propertyManager.getCallBackRedirectIndexUrl() + "/login");
        }
        String token = loginResDTO.getToken();
        // 前端vue-router hash模式下，后端得返回这种格式：http://localhost:5173/#/?Z-Token=eyJ0eXAiOiJKV1QiLCJ
        // 之前的history模式下，http://localhost:5173/?Z-Token=eyJ0eXAiOiJKV1QiLCJ 就可以试别了。
        ModelAndView modelAndView = new ModelAndView("redirect:" +
                propertyManager.getCallBackRedirectIndexUrl() + CommonConstant.TOKEN + "=" + token);
//        modelAndView.addObject(CommonConstant.TOKEN, token);
        return modelAndView;
    }

    /**
     * 获取当前权限下所有人员，超管能查所有
     */
    @GetMapping("listAll")
    public ApiResponse<List<UserVo>> listAll() {
        return ApiResponse.ok(userRoleMenuManager.listAll());
    }

    @MenuNeed(menuName = SUPER_ADMIN_ROLE)
    @GetMapping("manualExpireMinIOHandle")
    public ApiResponse manualExpireMinIOHandle() {
        minIOFileExpireHandleJob.fileExpireHandle();
        return ApiResponse.ok();
    }

    /**
     * 当前用户信息
     */
    @PostMapping("currentUserInfo")
    public ApiResponse<UserVo> currentUserInfo() {
        UserTokenInfo userTokenInfo = ContextCache.currentUser();
        return ApiResponse.ok(Optional.ofNullable(userTokenInfo).map(r -> {
            UserVo userVo = new UserVo();
            BeanUtil.copyProperties(r, userVo);
            return userVo;
        }).orElse(null));
    }

    /**
     * 获取当前用户菜单列表
     */
    @GetMapping("currentUserMenu")
    public ApiResponse<String> currentUserMenu() {
        // 这里是微信扫码登录后的请求查询菜单的入口，也是唯一入口，其他地方要确保不会调这个接口
        // 游客试用三天
        userRoleMenuManager.guestTrial();
        List<String> menuList = userRoleMenuManager.currentUserMenu(ContextCache.currentUserId());
        return ApiResponse.ok(JSONObject.toJSONString(menuList));
    }

    /**
     * 获取用户常驻城市
     *
     * @return
     */
    @GetMapping("getCitySetting")
    public ApiResponse<String> getCitySetting() {
        String city = userService.getCitySetting();
        return ApiResponse.ok(city);
    }

    /**
     * 用户配置常驻城市
     *
     * @return
     */
    @PostMapping("citySetting")
    public ApiResponse citySetting(@RequestBody CitySettingRequest citySettingRequest) {
        userService.citySetting(citySettingRequest);
        return ApiResponse.ok();
    }

    /**
     * 获取中国大陆293个地级市
     *
     * @return
     */
    @GetMapping("listAllCity")
    public ApiResponse listAllCity() {
        return ApiResponse.ok(CityConstant.CITY_RESULT_LIST);
    }

    /**
     * 获取城市的近三天的天气情况
     *
     * @return
     */
    @GetMapping("getWeatherByCity")
    public ApiResponse<WeatherAPIDetailDTO> getWeatherByCity(@RequestParam String city) {
        WeatherAPIDetailDTO dto = WeatherUtil.getRecent3DaysWeather(propertyManager.getWeatherApiKey(), city);
        return ApiResponse.ok(dto);
    }

    @SneakyThrows
    private AuthRequest getAuthRequest() {
        return new AuthWeChatOpenRequest(AuthConfig.builder()
                .clientId(propertyManager.getWxClientId())
                .clientSecret(propertyManager.getWxClientSecret())
                .redirectUri(propertyManager.getWxNotifyUrl() + "/user/callback")
//                .redirectUri("http://www.zray.fun:8888/weixin/callback")
                .build());
    }
}
